Your testbench must be separate from the design — not just logically, but also temporally.
Real Hardware Analogy
In a real hardware system:
- Storage elements (like flip-flops) latch inputs on the active clock edge.
- The latched values propagate through the logic network to the next storage elements.
- The propagation from one storage input to the next must complete within one clock cycle.
A hardware tester mimics this timing behavior by:
- Driving the chip’s inputs just after the clock edge.
- Sampling the outputs just before the next clock edge.
Testbench Behavior
A testbench should behave like another storage element plus logic:
- Drive signals after the active clock edge.
- Sample signals as late as possible (just before the next edge),
following the protocol timing specification.
However, if both the DUT (Design Under Test) and testbench are written purely in Verilog modules, this ideal timing is nearly impossible to achieve.
The Race Condition Problem
If the testbench drives signals exactly at the clock edge, race conditions can occur:
- The clock may reach different DUT inputs at slightly different times.
- From the simulator’s point of view, all edges occur simultaneously.
- But in the DUT:
- Some inputs may capture previous-cycle values.
- Others may capture current-cycle values.
This leads to unpredictable and non-deterministic behavior.
Common (but Problematic) Workarounds
1. Using #0 Delays
- Adding a small delay such as
#0can force a thread to pause and reschedule after all other concurrent processes. - This ensures the testbench drives after the design’s operations.
- However:
2. Using Larger Delays (#1)
- Some designs attempt to solve the problem with
#1delays. - Since RTL code is typically event-driven with no intra-cycle timing, a
#1delay allows logic to “settle.” - But — the meaning of
#1depends on the time precision of each module:- One module might interpret
#1as 1 ns. - Another might interpret it as 10 ps.
- One module might interpret
- This makes synchronization inconsistent across modules.